package br.com.spam.pipeline.instruction;

import java.util.Map;

import br.com.spam.pipeline.ConverterHelper;
import br.com.spam.pipeline.core.ControlSignal;
import br.com.spam.pipeline.core.Processor;
import br.com.spam.pipeline.core.Register;
import br.com.spam.pipeline.core.RegisterDataSource;

public abstract class RTypeInstruction extends Instruction
{
	private final Register rs, rt, rd;
	private final RsRtLoader loader;
	private int executionClocks = 0;

	public RTypeInstruction(String instruction, Processor processor)
	{
		super(instruction, processor);
		this.rs = processor.getRegister(ConverterHelper.stringToInt(instruction.substring(6, 11)));
		this.rt = processor.getRegister(ConverterHelper.stringToInt(instruction.substring(11, 16)));
		this.rd = processor.getRegister(ConverterHelper.stringToInt(instruction.substring(16, 21)));
		this.loader = new RsRtLoader(this.rs, this.rt, this);
	}

	@Override
	public String getInstructionAsString()
	{
		return String.format("I%d: %s %s, %s, %s", this.index(), this.name(), this.rs.name(),
				this.rt.name(), this.rd.name());
	}
	@Override
	protected boolean afterInstructionDecode()
	{
		return this.loader.instructionDecodeHook();
	}

	@Override
	public boolean execute()
	{
		this.loader.executionHook();

		++this.executionClocks;
		this.rd.lock();

		if (this.executionClocks == this.operationDuration())
		{
			this.rd.write(this.operation(this.loader.rsValue(), this.loader.rtValue()),
					RegisterDataSource.JustCalculated);
			return true;
		}

		return false;
	}
	protected int operationDuration()
	{
		return 1;
	}

	protected abstract int operation(int rs, int rd);

	protected abstract int getALUOp();

	@Override
	public void generateControlSignals(Map<ControlSignal, Object> controlSignals)
	{
		controlSignals.put(ControlSignal.RegDst, 1);
		controlSignals.put(ControlSignal.ALUSrcB, 0);
		controlSignals.put(ControlSignal.MemToReg, 0);
		controlSignals.put(ControlSignal.RegWrite, 1);
		controlSignals.put(ControlSignal.MemWrite, 0);
		controlSignals.put(ControlSignal.Branch, 0);
		controlSignals.put(ControlSignal.Jump, 0);
		controlSignals.put(ControlSignal.ALUOp, this.getALUOp());
	}
}
